<?php

Raise::load('core.RaiseObject');
Raise::load('core.text.regex.RaiseRegexMatch');
Raise::load('core.text.regex.RaiseRegexMatchCollection');
Raise::load('core.reflection.RaiseVariable');
Raise::load('core.collection.RaiseCollection');
Raise::load('core.RaiseString');

/**
 * RaiseRegex class
 * provides functionality to regular expression matching and replacement
 *
 * @author Sam-Mauris Yong / hellclanner at live dot com
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package phpraise.core.text.regex
 * @since 1.2
 */
class RaiseRegex extends RaiseObject {

    /**
     * The regular expression that is used in this RaiseRegex
     * @var string
     */
    private $regex;

    /**
     * Create a new RaiseRegex
     * @param string $regex The pattern that this RaiseRegex will use
     */
    public function __construct($regex){
        $this->regex($regex);
    }

    /**
     * Get or set the regular expression that is used in this RaiseRegex
     * @param string $r (optional) If present, the old value will be overriden by this value.
     * @return string
     */
    public function regex($r = null){
        if(func_num_args() == 1){
            $this->regex = $r;
        }
        return $this->regex;
    }

    /**
     * Perform a perl-compatible regular expression (PCRE) match to match within the subject
     * @param string $subject The input string
     * @return RaiseRegexMatchCollection A RaiseRegexMatchCollection that contains all the RaiseRegexMatch found in the subject
     * @link http://php.net/manual/en/function.preg-match.php
     */
    public function match($subject){
        preg_match($this->regex(), $subject, $m);
        $result = new RaiseRegexMatchCollection();
        foreach($m as $a){
            $result->add(new RaiseRegexMatch($this, $a));
        }
        return $result;
    }

    /**
     * Perform a perl-compatible regular expression (PCRE) match to match all matches within the subject
     * @param string $subject The input string
     * @return RaiseCollection The collection of RaiseRegexMatchCollection
     * @link http://www.php.net/manual/en/function.preg-match-all.php
     */
    public function matchAll($subject){
        preg_match_all($this->regex(), $subject, $m, PREG_SET_ORDER);
        $finalResult = new RaiseCollection();
        foreach($m as $a){
            $result = new RaiseRegexMatchCollection();
            foreach($a as $c){
                $result->add(new RaiseRegexMatch($this, $c));
            }
            $finalResult[] = $result;
        }
        return $finalResult;
    }

    /**
     * Perform a perl-compatible regular expression search and replace
     * @param string $subject The input string
     * @param string|array|RaiseCollection $replacement The string or collection of replacements
     * @param integer $limit (Optional) If set, the number of search and replace operations will be limited by this limit.
     * @return string|RaiseCollection
     * @link http://www.php.net/manual/en/function.preg-replace.php
     */
    public function replace($subject, $replacement, $limit = -1){
        $r = preg_replace($this->regex, $replacement, $subject, $limit);
        if(RaiseVariable::isType($r, 'array')){
            $r = new RaiseCollection($r);
        }
        return $r;
    }

    /**
     * Perform a perl-compatible regular expression search and replaceusing a callback
     * @param string $subject The input string
     * @param callback $callback A callback that will be called and passed an array of matched elements in the subject string. The callback should return the replacement string.
     * @param integer $limit (Optional) If set, the number of search and replace operations will be limited by this limit.
     * @return string|RaiseCollection
     * @link http://www.php.net/manual/en/function.preg-replace-callback.php
     */
    public function replaceCallback($subject, $callback, $limit = -1){
        $r = preg_replace_callback($this->regex, $callback, $subject, $limit);
        if(RaiseVariable::isType($r, 'array')){
            $r = new RaiseCollection($r);
        }
        return $r;
    }
    
    /**
     * Find the position of the first occurrance of perl compatible regular expression match in the string
     * @param RaiseString|string $s The string to search for
     * @param integer $offset (optional) The position to start searching for
     * @return integer A non-negative number indicating the position of $s in the string, or -1 if not found. 
     */
    public function indexOf($subject, $offset = 0){
        if(!($subject instanceof RaiseString)){
            $subject = new RaiseString($subject);
        }
        $i = preg_match($this->regex(), $subject, $m, PREG_OFFSET_CAPTURE , $offset);
        if($i){
            return $subject->indexOf($m[0][0], $offset);
        }
        return -1;
    }
    
    /**
     * Find the position of the last occurrance of perl compatible regular expression match in the string
     * @param RaiseString|string $s The string to search for
     * @param integer $offset (optional) The position to start searching for
     * @return integer A non-negative number indicating the position of $s in the string, or -1 if not found. 
     */
    public function lastIndexOf($subject, $offset = 0){
        if(!($subject instanceof RaiseString)){
            $subject = new RaiseString($subject);
        }
        $i = preg_match_all($this->regex(), $subject, $m, PREG_SET_ORDER , $offset);
        if($i){
            $m = end($m);
            return $subject->lastIndexOf($m[0], $offset);
        }
        return -1;
    }

    /**
     * Escapes / add slashes to special characters in Regex operation
     * @param string $text The text to perform escaping
     * @return string
     * @static
     */
    public static function escape($text) {
        $spec  = '"\'^$.[]|()?*+{}/!';
        $text = str_replace("\\n", "\n", $text);
        $text = addcslashes($text, $spec);
        $text = htmlentities($text);
        return $text;
    } 
    
}
